(function (definition) {
    // RequireJS
    if (typeof define == "function") {
        define(definition);
    } else {
        definition();
    }
})(function() {
	window.le = {};
});(function (le, $_) {
	var Class = $_.Base.Class;
	function get(key) {
		return this._attributes[key];
	}
	function has(key){
		return get(key) != null;
	}
	function set(key, value, callback) {
		var error = this.validate(key, value);
		if(error){
			this._attributes[key] = value;
		}
		if(callback) callback(error, key, value);
	}
	function validate(key, value){
		if(!this._validator[key]) return true;
		var error = this._validator[key](value);
		if(error === undefined) return true;
		return error;
	}
	function addValidate(key, fn) {
		this._validator[key] = fn;
	}
	function removeValidate(key, fn){
		delete this._validate[key];
	}
	function fetch(callback) {
		
	}
	function save(callback) {
		
	}
	function keys(callback) {
		return $_.O.keys(_attributes);
	}
	function initialize(attr, validator) {
		if(attr){
			this._attributes = $_.O.clone(attr);
		}
		if(validator){
			this._validator = validator;
		}
	}
	var Model = Class({
		_attributes: {},
		_validator: {},
		_url: '',
		get:get,
		set: set,
		has: has,
		validate:validate,
		keys: keys,
		fetch: fetch,
		save: save,
		initialize: initialize
	});
	le.Model = Model;
})(le, Asdf);(function (le, $_) {
	var Class = $_.Base.Class, extend = $_.O.extend ;
	viewHelper = {
		removeAllEvent : function(element, key) {
			$_.Event.removeAll(element, key);
		},
		addEvents : function(element, events) {
			var key;
			for (key in events) {
				var method = events[key];
				if (!$_.O.isFunction(method))
					throw new TypeError("event handler Error.");
				method = $_.F.bind(method, self);
				$_.Event.on(element, key, method);
			}
		},
		empty : function(element) {
			element.innerHTML = '';
			viewHelper.removeAllEvent(element);
		},
		domBind : function(element, el, strings) {
			if ($_.O.isString(el)) {
				el = $_.S.toDocumentFragment(el);
			}
			if (el.nodeType === 1 || el.nodeType === 9 || el.nodeType === 11)
				element.appendChild($_.Template.bind(el, strings));
		},
		render : function(element) {
			var parentElement;
			parentElement = self.parentElement;
			if ($_.O.isString(self.parentElement)) {
				parentElement = document.getElementById(self.parentElement);
			}
			if (!element.parentNode || element.parentNode !== parentElement) {
				parentElement = parentElement || document.body;
				parentElement.appendChild(element);
			}
		}
	};
	function render(data, callback) {
		var clone = $_.O.clone;
		var strings = clone(this.getStrings()), html = this.getHtml(), events = clone(this.getEvents());
		extend(strings, data&&data.strings);
		extend(events, data&&data.events);
		if($_.O.isFunction(callback)){
			callback(this.element, {strings: strings, html: html, events:events});
		} else {
			this.element.empty();
			this.element.domBind(html, strings);
			this.element.addEvents(events);
			this.element.render();
		}
	}
	
	function getHtml() {
		var mode = this.conf[this.mode], html;
		if(mode && (html = mode.html ) ) {
			return html;
		}
		return this.conf['default'].html;
	}
	function setHtml(id, html) {
		var mode = this.conf[this.mode];
		if(mode) {
			mode.html = html;
		}
		throw new Error('default can\'t setting');
	}
	
	function getEvents() {
		var mode = this.conf[this.mode], events;
		if(mode && (events = mode.events))
			return events;
		return this.conf['default'].events;
	}
	function setEvents(events) {
		var mode = this.conf[this.mode];
		if(mode) {
			if(mode.events) mode.events = {};
			extend (mode.events, events);
		}
		throw new Error('default can\'t setting');
	}	
	
	function getStrings() {
		var mode = this.conf[this.mode], strings;
		if(mode && (strings = mode.strings))
			return strings;
		return this.conf['default'].strings;
	}
	function setStrings(string){
		var mode = this.conf[this.mode];
		if(mode) {
			if(mode.strings) mode.strings = {};
			extend (mode.strings, string);
		}
		throw new Error('default can\'t setting');
	}
	
	function setVM(vm) {
		this.vm = vm;
		vm.view = this;
	}
	function changeMode(mode) {
		this.mode = mode;
	}
	function initialize(options) {
		var self = this, methodize = $_.F.methodize;
		function make(obj) {
			if(!(obj && obj.tagName)) return null;
			var key, el = document.createElement(obj.tagName);
			if(el.className)el.className = obj.className;
			if (obj.attr) {
				for(key in obj.attr){
					 el.setAttribute(key, obj.attr[key]);
				}
			}
			return el;
		}
		function configure(options) {
			if(options.element){
				if($_.O.isElement(options.element)){
					self.element = options.element;
				}else {
					self.element = make(options.element);
				}
			}
			
			$_.A.each({
				removeAllEvent : function(element, key) {
					$_.Event.removeAll(element, key);
				},
				addEvents : function(element, events) {
					var key;
					for (key in events) {
						var method = events[key];
						if (!$_.O.isFunction(method))
							throw new TypeError("event handler Error.");
						method = $_.F.bind(method, self);
						$_.Event.on(element, key, method);
					}
				},
				empty : function(element) {
					element.innerHTML = '';
					viewHelper.removeAllEvent(element);
				},
				domBind : function(element, el, strings) {
					if ($_.O.isString(el)) {
						el = $_.S.toDocumentFragment(el);
					}
					if (el.nodeType === 1 || el.nodeType === 9 || el.nodeType === 11)
						element.appendChild($_.Template.bind(el, strings));
				},
				render : function(element) {
					var parentElement;
					parentElement = self.parentElement;
					if ($_.O.isString(self.parentElement)) {
						parentElement = document.getElementById(self.parentElement);
					}
					if (!element.parentNode || element.parentNode !== parentElement) {
						parentElement = parentElement || document.body;
						parentElement.appendChild(element);
					}
				}
			}, function (value, key){
				self.element[key] = methodize(value);
			});
			
			if(options.parentElement){
				self.parentElement = options.parentElement;
			}
			if(options.vm){
				self.setVM(options.vm);
			}
			if(options.conf) {
				extend(self.conf, options.conf, true);
			}
		}
		this.element = document.createElement('div');
		if(options === void 0) return;
		configure(options);
	}
	
	var View = Class( {
		conf: {
			'default' : {
				html: '',
				events: {},
				strings: {}
			}
		},
		mode: 'default',
		vm: null,
		element: null,
		parentElement: null,
		render: render,
		setVM: setVM,
		changeMode: changeMode,
		getHtml: getHtml,
		setHtml: setHtml,
		getEvents: getEvents,
		setEvents: setEvents,
		getStrings: getStrings,
		setStrigns: setStrings,
		setVM: setVM,
		initialize: initialize
	});
	le.View = View;
})(le, Asdf);(function (le, $_) {
	var Class = $_.Base.Class;
	function on(eventName, listener){
		if(!$_.O.isString(eventName))
			return null;
		if(!$_.O.isFunction(listener))
			return null;
		if(this.event[eventName] === void 0)
			this.event[eventName] = [];
		this.event[eventName].push($_.F.bind(listener, this));
	}
	function once(eventName, listener){
		tfn = $_.F.wrap(listener, function(fn){
			var arg = [].slice.call(arguments, 1);
			ofn.apply(this, arg);
			remove(eventName, tfn);
		});
		this.on(eventName, tfn);
	}
	function remove(eventName, listener){
		$_.A.without(this.event[eventName], listener);
	} 
	function removeAll(eventName){
		this.event[eventName].length = 0;
	}
	function listeners(eventName){
		return this.event[eventName];
	}
	function emit(eventName, data){
		this.doFilter();
		var runable = this.event[eventName], i;
		for(i = 0; runable&&i < runable.length; i++){
			runable[i](data);
		}
	}
	function addFilter(filter){
		this.filters.push(filter);
	}
	function removeFilter(filter){
		this.filters = $_.A.without(this.filters, filter);
	}
	function doFilter() {
		var i;
		for(i = 0 ; i < this.filters.length; i++ ){
			this.filters[i].call(this,this);
		}
	}
	var EventEmitter = Class({
		initialize: function () {
			this.event = {any: []};
			this.filters = [];
		},
		event:null,
		filters: null,
		on: on,
		once: once,
		remove: remove,
		removeAll: removeAll,
		listeners: listeners,
		emit:emit,
		addFilter: addFilter,
		removeFilter: removeFilter,
		doFilter:doFilter
	});
	le.EventEmitter = EventEmitter;
})(le, Asdf);(function (le, $_) {
	var Class = $_.Base.Class, wrap = $_.F.wrap;
	
	function getVmById(id){
		var children  = this.children, i, finded;
		if(this.id === id) return this;
		else {
			for(i = 0; i < children.length; i++) {
				if(finded = children[i].getVmById(id)) return finded;
			}
		}
		return null;
	}
	function getRoot() {
		var vm = this;
		while(vm = vm.parent, vm.parent);
		return vm;
	}
	
	function notify(){
		var i, data, fn;
		var arg = $_.A.toArray(arguments);
		if($_.O.isPlainObject(arg[0]))
			data = arg.shift();
		if($_.O.isFunction(arg[0]))
			fn = arg.shift();
		
		this.view.render(data, fn);
		for(i = 0; i < this.children.length; i++ ){
			this.children[i].notify.apply(this.children[i], arg);
		}
	}
	
	function getData(key) {
		return this.model.get(key);
	}
	function setData(key, value){
		this.model.set(key, value);
	}
	
	function appendChild(child) {
		child.parent = this;
		this.children.push(child);
	}
	function removeChild(child) {
		this.children = $_.A.without(this.children, child);
	}
	
	function setModel(model) {
		this.model = model;
	}
	function setView(view) {
		this.view = view;
		view.vm = this;
	}
	
	function initialize(options){
		var self= this;
		this.id = "c"+$_.Utils.makeuid();
		if(!options) return;
		if(options.id)this.id = options.id;
		if(options.view) this.setView(options.view);
		if(options.filters) {
			$_.A.each(options.filters, function (method, name){
				self.addFilter(method);
			});
		}
		if(options.model) this.setModel(model);		
		else if(options.data){ this.setModel(new le.Model(options.data));}
		else this.setModel(new le.Model());
	}
	var extendmethod = {};
	$_.A.each($_.A, function(method, name ) {
		extendmethod[name] = wrap(method, function(fn){
			return this.children = fn.apply(this, [this.children].concat([].slice.call(arguments, 1)));
		}
	);
	});
	
	var VM = Class(le.EventEmitter, {
		id: null,
		initialize: initialize,
		view: null,
		parent: null,
		children: [],
		getVmById:getVmById,
		notify: notify,
		getData: getData,
		setData: setData,
		setModel: setModel,
		setView: setView,
		getRoot: getRoot,
		appendChild: appendChild,
		removeChild: removeChild
	});
	VM.include(extendmethod);
	le.VM = VM;
})(le, Asdf);